/*
  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
  PARTICULAR PURPOSE.
  
    This is sample code and is freely distributable.
*/
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using svs.Mobile.BeyondMedia.FeedCore;
using svs.Mobile.BeyondMedia.FeedCore.RSS;
using svs.Mobile.BeyondMedia.GraphicsAPI;
using svs.Mobile.BeyondMedia.Objects;
using svs.Mobile.BeyondMedia.Properties;

namespace svs.Mobile.BeyondMedia.Controls
{
	

	/// <summary>
	/// Provide a 2 line listview
	/// </summary>
	internal class FeedsView : OwnerDrawnList<Feed>, IFeedsView
	{
		protected const int FONT_SIZE = 10;
		private readonly CategoryPanel m_CategoryPanel = new CategoryPanel();
		private readonly Timer m_EnterTimer = new Timer();
		protected Feed m_Feed;
		protected bool m_IsLocalKeyUp = false;
		public event Helpers.FeedSelectedEvent Select;
		public event Helpers.NavigationEvent Navigation;

		#region Nested type: ReloadDelegate

		private delegate void ReloadDelegate();

		#endregion

		#region Nested type: TrackCountChangedDelegate

		private delegate void TrackCountChangedDelegate(Track i_track);

		#region Event Handlers

		private void OnNavigation(Helpers.NavigationDirection i_direction)
		{
			if (Navigation != null)
				Navigation(this, i_direction);
		}

		#endregion

		#region Constructors

		public FeedsView(bool supportCategories)
		{
			// We need a total of 5 rows, so the height is 180/5=36
			using (Graphics g = CreateGraphics())
			{
				using (Font font = new Font(Font.Name, FONT_SIZE, FontStyle.Bold))
				{
					// Calc line height to be height of letter A plus 10%
					int fontHeight = (int) (g.MeasureString("A", font).Height*1.1);
					ItemHeight = fontHeight*2;
				}
			}

			FeedRepository.TrackDeleted += OnTrackCountChanged;
			FeedRepository.TrackAdded += OnTrackCountChanged;

			if (supportCategories)
			{
				m_EnterTimer.Tick += OnShowCategoryList;
				m_EnterTimer.Interval = 700;
				BuildCategoryPanel();
				FeedImageList.Initialize(new Size(ItemHeight, ItemHeight));
			}
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Gets the selected feed.
		/// </summary>
		/// <value>The selected feed.</value>
		public Feed SelectedFeed
		{
			get
			{
				if (SelectedIndex >= 0 && SelectedIndex < Items.Count)
					return Items[SelectedIndex];
				else
					return null;
			}

			set
			{
				int index = -1;

				if (Items.Count > 0)
				{
					if (value != null)
					{
						index = Items.IndexOf(value);
					}

					if (index == -1)
						index = 0;
				}
				SelectedIndex = index;
				EnsureVisible(SelectedIndex);
			}
		}

		/// <summary>
		/// The current Feed
		/// </summary>
		public Feed Feed
		{
			set { SetRootFeed(value, SelectedIndex); }
			get { return m_Feed; }
		}

		public override string OnEmptyText
		{
			get
			{
				if (Configuration.ActiveFeedCategory == CategoryManager.AllFeeds)
					return Resources.FeedViewIsEmpty;
				else
					return Resources.FeedViewCategoryIsEmpty;
			}
		}

		/// <summary>
		/// The name of the window
		/// </summary>
		public string Title
		{
			get
			{
				if (Feed == null || Feed.ParentFeed == null)
					return Configuration.ActiveFeedCategory.Name + " (" + Items.Count + ")";
				else
					return Feed.ParentFeed.Name;
			}
		}

		/// <summary>
		/// The menu
		/// </summary>
		public MainMenu ViewMenu
		{
			get { return MenuManager.GetMenu(Helpers.ViewNames.Feeds); }
		}

		#endregion

		#region Public Methods

		/// <summary>
		/// Gets a screenshot of the view for transitions
		/// </summary>
		/// <returns></returns>
		public Bitmap CreateScreenShot()
		{
			return GraphicsHelper.CreateControlSnapshot(this);
		}

		public void OnAfterDeactivate(){}

		public void OnAfterActivate()
		{
		}

		public void OnBeforeActivate()
		{
			Focus();
		}

		public void Clear()
		{
			m_Feed = null;
			Items.Clear();
			SelectedIndex = -1;
			Refresh();
		}

		public void Reload()
		{
			if (Master.MasterForm.InvokeRequired)
			{
				Master.MasterForm.Invoke(new ReloadDelegate(Reload));
				return;
			}

			if (Feed == null)
				return;

			Feed = Feed;
		}

		/// <summary>
		/// Sets the current feed, optionally specifying a selected feed by index
		/// </summary>
		public void SetRootFeed(Feed root, int selectedIndex)
		{
			SetRootFeed(root);

			if (Items.Count > 0)
			{
				if (selectedIndex >= 0 && selectedIndex < Items.Count)
					SelectedFeed = Items[selectedIndex];
				else if (selectedIndex >= Items.Count)
					SelectedFeed = Items[Items.Count - 1];
				else
					SelectedFeed = Items[0];
			}
			else
				SelectedFeed = null;
		}

		/// <summary>
		/// Sets the current feed, optionally specifying a selected feed
		/// </summary>
		public void SetRootFeed(Feed root, Feed selected)
		{
			SetRootFeed(root);
			SelectedFeed = selected;
		}

		/// <summary>
		/// The current Feed
		/// </summary>
		public void SetRootFeed(Feed root)
		{
			m_Feed = root;
			Items.Clear();

			if (root == null)
				return;

			FilterByCategory();

			if(m_Feed.CanNavigateUp)
				Items.Insert(0, FeedRepository.GoUpPlaceholder);

			OnResize(null);
			ViewManager.RefreshTitle();
		}

		public void ShowCategoryList()
		{
			m_CategoryPanel.ShowList();
		}

		public Feed GetNextUnreadFeed()
		{
			if (SelectedIndex >= 0 && Items.Count > 0)
			{
				int i = SelectedIndex == Items.Count-1?0:SelectedIndex + 1;

				while (i != SelectedIndex)
				{
					if (Items[i].HasUnreadItems)
						return Items[i];

					if (++i == Items.Count)
						i = 0;
				}
			}
			return null;
		}

		public Feed GetNextFeed()
		{
			if (SelectedIndex < Items.Count-1)
				return Items[SelectedIndex + 1];

			return null;
		}

		public Feed GetPreviousFeed()
		{
			if (SelectedIndex > 0)
				return Items[SelectedIndex - 1];

			return null;
		}

		public void NotifyFeedStatusChanged(Feed feed)
		{
			Refresh();
		}

		#endregion

		#region Protected Methods

		protected virtual void FilterByCategory()
		{
			Items.AddRange(m_Feed.Feeds.FindAll(CategoryManager.IsInActiveCategory));
		}

		protected void BuildCategoryPanel()
		{
			Controls.Add(m_CategoryPanel);
			m_CategoryPanel.CategorySelected += OnCategorySelected;
		}

		private void OnCategorySelected(FeedCategory category)
		{
			m_CategoryPanel.HideList();

			if (category != null && category != Configuration.ActiveFeedCategory)
			{
				Configuration.ActiveFeedCategory = category;

				if (Feed != FeedRepository.RootFeed)
				{
					Feed = FeedRepository.RootFeed;
				}
				else
				{
					Reload();
				}
			}
		}

		protected void OnShowCategoryList(object sender, EventArgs e)
		{
			m_EnterTimer.Enabled = false;
			m_IsLocalKeyUp = false;
			ShowCategoryList();
		}

		protected override void OnResize(EventArgs e)
		{
			base.OnResize(e);

			Rectangle r = ClientRectangle;

			r.Inflate(-20, -10);

			m_CategoryPanel.Size = r.Size;
			m_CategoryPanel.Location = r.Location;
		}

		protected override string GetItemText(int ItemIndex)
		{
			Feed f = Items[ItemIndex];
			if (f != null)
				return f.Name;

			return string.Empty;
		}

		protected override void OnKeyUp(KeyEventArgs e)
		{
			m_EnterTimer.Enabled = false;

			try
			{
				switch (e.KeyCode)
				{
					case Keys.Return:

						if (Select != null && SelectedIndex != -1 && m_IsLocalKeyUp)
						{
							Select(SelectedFeed, FeedViewType.RSS);
						}

						e.Handled = true;
						break;
				}

				if (!e.Handled)
				{
					base.OnKeyUp(e);
				}
			}
			finally
			{
				m_IsLocalKeyUp = false;
			}
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Return:

					m_IsLocalKeyUp = true;
					m_EnterTimer.Enabled = true;
					e.Handled = true;
					break;

					//Left Right
				case Keys.Left:
					OnNavigation(Helpers.NavigationDirection.Back);
					e.Handled = true;
					break;

					//Left Right
				case Keys.Right:
					OnNavigation(Helpers.NavigationDirection.Forward);
					e.Handled = true;
					break;
			}

			if (!e.Handled)
			{
				base.OnKeyDown(e);

				if (!e.Handled)
					e.Handled = ShortcutKeyManager.HandleKey(e.KeyCode);
			}
		}

		protected override void ItemClicked(int i_ItemIndex, int X, int Y, DragHelper.ClickSpeed speed)
		{
			base.ItemClicked(i_ItemIndex, X, Y, speed);

			if (Select != null && speed == DragHelper.ClickSpeed.Fast)
			{
				if (X > ClientRectangle.Width - (ClientRectangle.Width/4))
				{
					CommandManager.CmdPlaySelectedFeed();
				}
				else
					Select(SelectedFeed, FeedViewType.RSS);
			}
		}

		/// <summary>
		/// Custom OnPaint. This paints the listview items
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPaint(PaintEventArgs e)
		{
			// Declare vars
			Font fontMainItem;
			Font fontNumber;
			Image bmp, overlay = null, warnoverlay = null;

			int Column2Left = ItemHeight + 5;

			using (Graphics gOffScreen = PrepareOffscreenGraphics())
			{
				// Set the y pos of the current item
				int itemTop = 0;

				// Draw the visible items.
				for (int n = VScrollBar.Value; n <= VScrollBar.Value + DrawCount; n++)
				{
					if (Items.Count == 0 || n > Items.Count - 1)
						break;

					// Get the current MailItem
					Feed feed = (Feed) Items[n];

					Rectangle itemArea;
					itemArea = new Rectangle(0,
					                         itemTop,
					                         ClientSize.Width - (VScrollBar.Visible ? VScrollBar.Width : 0),
					                         ItemHeight);

					// Draw the selected item to appear highlighted
					Color fontColor;

					if (n == TouchedIndex)
					{
						DrawItemBox(gOffScreen,
						            ItemSeparatorColor,
						            false,
						            true,
						            BackColor,
						            itemArea);
						fontColor = ForeColor;
					}
					else if (n == SelectedIndex)
					{
						DrawItemBox(gOffScreen,
						            ItemSeparatorColor,
						            true,
						            false,
						            BackColor,
						            itemArea);
						fontColor = CalcTextColor(SystemColors.MenuText);
					}
					else
					{
						fontColor = ForeColor;
						DrawItemBox(gOffScreen,
						            ItemSeparatorColor,
						            false,
						            false,
						            BackColor,
						            itemArea);
					}

					bmp = Resources.DefaultFeedImage;

					/*switch (feed.Type)
					{
						case FeedType.LocalFolder:
							bmp = feed.IsSubFeed ? FeedImageList.DefaultSubAudioFolderImage : FeedImageList.DefaultAudioFolderImage;
							break;

						case FeedType.RSSFeed:
							bmp = FeedImageList.GetFeedImageFor(feed);
							break;

						case FeedType.RSSFeedWithPodcasts:
							bmp = FeedImageList.GetFeedImageFor(feed);
							break;

						case FeedType.OPMLFeed:
							bmp = FeedImageList.DefaultOpmlImage;
							break;

						default:
							bmp = FeedImageList.DefaultFeedImage;
							break;
					}*/

					Rectangle imgRect = new Rectangle(0, itemTop, bmp.Width, bmp.Height);
					gOffScreen.DrawImage(bmp, imgRect, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, new ImageAttributes());

					int podcastImageStart = 0;
					if (feed.AllowEnclosures && feed.Tracks.Count > 0)
					{
						Image podcasts = Resources.podcasts;
						Rectangle podcastRect =
							new Rectangle((itemArea.Width - bmp.Width) + (bmp.Width - podcasts.Width)/2 + 3,
							              itemArea.Top + 2, podcasts.Width,
							              podcasts.Height);

						podcastImageStart = itemArea.Width - podcastRect.Left;

						ImageAttributes iap = new ImageAttributes();
						iap.SetColorKey(Color.White, Color.White);
						gOffScreen.DrawImage(podcasts, podcastRect, 0, 0, podcasts.Width, podcasts.Height, GraphicsUnit.Pixel,
						                     iap);
					}

					//Draw the lock overlay for locked feeds
					if (!feed.CanDeleteTracks)
					{
						overlay = Resources.locked;
						ImageAttributes iao = new ImageAttributes();
						iao.SetColorKey(Color.White, Color.White);
						Rectangle overlayRect =
							new Rectangle(imgRect.Left, imgRect.Bottom - overlay.Height, overlay.Width, overlay.Height);
						gOffScreen.DrawImage(overlay, overlayRect, 0, 0, overlay.Width, overlay.Height, GraphicsUnit.Pixel, iao);
					}

					//Draw the name of the feed
					fontMainItem = new Font(Font.Name, FONT_SIZE, feed.HasUnreadItems ? FontStyle.Bold : FontStyle.Regular);
					Rectangle nameRect =
						new Rectangle(Column2Left, itemTop, itemArea.Width - (Column2Left + podcastImageStart), ItemHeight);

					string name = feed.Name;
					if (Width <= 240 && name.Length > 30)
						name = name.Substring(0, 30) + "...";
					else if (Width > 240 && name.Length > 55)
						name = name.Substring(0, 55) + "...";

					using (Brush b = new SolidBrush(fontColor))
					{
						gOffScreen.DrawString(name,
						                      fontMainItem,
						                      b, nameRect);
					}

					// Draw the number of feeds/tracks
					StringFormat sf = new StringFormat();
					sf.Alignment = StringAlignment.Far;
					sf.LineAlignment = StringAlignment.Far;
					itemArea.Inflate(-6, -2);

					fontNumber = new Font(Font.Name, FONT_SIZE - 2, FontStyle.Regular);

					if (feed.UpdateFailed || feed.DownloadFailed)
					{
						warnoverlay = Resources.warn;
						ImageAttributes iao = new ImageAttributes();
						iao.SetColorKey(Color.White, Color.White);
						Rectangle warnoverlayRect =
							new Rectangle(imgRect.Right - warnoverlay.Width - 1,
							              imgRect.Bottom - warnoverlay.Height - 1, warnoverlay.Width, warnoverlay.Height);
						gOffScreen.DrawImage(warnoverlay, warnoverlayRect, 0, 0, warnoverlay.Width, warnoverlay.Height, GraphicsUnit.Pixel,
						                     iao);
					}

					string InfoText = null;
					//For the feed that is currently updating
					if (RssFeedUpdateManager.IsUpdating && RssFeedUpdateManager.CurrentFeed == feed)
					{
						if (feed.UpdateSize <= 0)
							InfoText = string.Format("Updating {0}...",  feed.UpdatedPortion > 0? CoreHelper.GetFileLengthAsString(feed.UpdatedPortion) : string.Empty);
						else
							InfoText =  string.Format("Updating {0}% of {1}...", (int)feed.UpdatePercent, CoreHelper.GetFileLengthAsString(feed.UpdateSize));
					}
						// For the feed that is currently downloading
					else if (EnclosureDownloadManager.IsDownloading && EnclosureDownloadManager.CurrentFeed == feed)
						//the current downloading feed
					{
						InfoText =
							string.Format("Downloading {0} of {1}...", feed.NumDownloadedEnclosures + 1,
							              feed.NumEnclosuresAvailableForDownload);
					}
						// feeds that are waiting to be downloaded
					else if (EnclosureDownloadManager.IsDownloading && !feed.DownloadFailed && feed.NumEnclosuresAvailableForDownload > 0 &&
					         feed.NumDownloadedEnclosures == 0)
					{
						InfoText = string.Format("{0} Pending...", feed.NumEnclosuresAvailableForDownload);
					}
						// general case where a feed can have podcasts and does not have child feeds
					else if (feed.DownloadPathExists && feed.Feeds.Count == 0)
					{
						string newencl = string.Empty;

						if (feed.NumEnclosuresAvailableForDownload > 0)
						{
							if (feed.DownloadFailed)
							{
								if (feed.NumDownloadedEnclosures == 0)
									newencl = (feed.NumEnclosuresAvailableForDownload - feed.NumDownloadedEnclosures) + " Failed!   ";
								else if (feed.Tracks.Count > 0)
									newencl = string.Format("{0} of {1} New   ", feed.NumDownloadedEnclosures,
									                        feed.NumEnclosuresAvailableForDownload);
							}
							else if (feed.NumDownloadedEnclosures > 0 && feed.Tracks.Count > 0)
								newencl = string.Format("{0} New   ", feed.NumDownloadedEnclosures);
						}

						InfoText =
							string.Format("{2}{0} {1}", feed.Tracks.Count > 0 ? feed.Tracks.Count + "," : string.Empty,
							              CoreHelper.FormatDate(feed.LastModifiedDate),
							              newencl);
					}
						// when the feed has child feeds
					else if (feed.DownloadPathExists)
					{
						InfoText = string.Format("{0} Feeds  {1}", feed.Feeds.Count, CoreHelper.FormatDate(feed.LastModifiedDate));
					}
						// for RSS feeds without podcasts
					else
					{
						InfoText = CoreHelper.FormatDate(feed.LastModifiedDate);
					}

					if (InfoText != null)
					{
						using (Brush b = new SolidBrush(fontColor))
						{
							gOffScreen.DrawString(InfoText, fontNumber, b, itemArea, sf);
						}
					}

					fontNumber.Dispose();

					// Cleanup
					fontMainItem.Dispose();
					bmp.Dispose();

					if (overlay != null)
						overlay.Dispose();

					if (warnoverlay != null)
						warnoverlay.Dispose();

					// Set the next item top to move down the item height
					itemTop += ItemHeight + 1;
				}

				// Now draw the visible list box
				e.Graphics.DrawImage(OffScreen, 0, 0);
			}

			base.OnPaint(e);
		}

		protected override void OnPaintBackground(PaintEventArgs e)
		{
		}

		protected void DrawItemBox(Graphics g, Color separatorColor, bool selected, bool touched, Color ItemBackground,
		                           Rectangle rc)
		{
			if (touched)
			{
				using (Brush br = new SolidBrush(ItemTouchedColor))
				{
					g.FillRectangle(br, rc);
				}
			}
			else if (selected)
			{
				GradientFill.Fill(g, rc, ItemHighLightColor1, ItemHighLightColor2, GradientFill.FillDirection.LeftToRight);
			}
			else
			{
				if (ItemBackground != BackColor)
				{
					using (Brush br = new SolidBrush(ItemBackground))
					{
						g.FillRectangle(br, rc);
					}
				}
			}

			using (Pen linePen = new Pen(separatorColor))
			{
				g.DrawLine(linePen, rc.Left,
				           rc.Bottom,
				           rc.Right,
				           rc.Bottom);
			}
		}

		private void OnTrackCountChanged(Track i_track)
		{
			if (InvokeRequired)
			{
				TrackCountChangedDelegate cbDel = OnTrackCountChanged;

				Invoke(cbDel, i_track);
				return;
			}

			OnResize(null);
			Refresh();
		}

		#endregion

		#endregion
	}
}